/**
 * 工具包功能实现
 */

#include <visa.h>
#pragma comment(lib, "visa64.lib")

#include "visatrx.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "VISATRX"
#include "share/log4app.h"

static size_t _visatrx_get_mtu(struct _transceiver* trx) {
    return 1024;
}

static std_err_t _visatrx_clear_rx_buffer(struct _transceiver* trx) {
    if(!trx || !trx->hif) return STD_ERR_INVALID_IF_TYPE;
    ViSession dev = (ViSession)trx->hif;
    /* Clear the device */
    ViStatus status = viClear(dev);
    return (status < VI_SUCCESS) ? STD_ERR_FAIL : STD_ERR_OK;
}

#include "share/pcre2_regexp.h"

int trx_regexp(const char* str, const char* pattern) {
    return regexp_match(str, pattern);
}

static rx_ret_t _visatrx_rx(struct _transceiver* trx, void* data, size_t size, size_t* len, long timeout, /*long lead,*/ long interval, const char* okFlag, rx_flag_t okFlagType, const char* failFlag, rx_flag_t failFlagType) {
    if(!trx || !trx->hif) return STD_ERR_INVALID_IF_TYPE;
    ViSession dev = (ViSession)trx->hif;

    /* Initialize the timeout attribute */
    viSetAttribute(dev, VI_ATTR_TMO_VALUE, timeout);
    ViUInt32 wlen = 0;
    
    // 接收数据
    ViStatus status = viRead(dev, data, size, &wlen);
    if (status == VI_ERROR_TMO || status == VI_SUCCESS) {
        if (len) *len = wlen;
        if (wlen >= size) return RX_RET_FULL;
        
        if (wlen && okFlag && okFlagType != RX_FLAG_NULL) {
            // 需要考虑 okFlag
            puts(data);
            if ((okFlagType == RX_FLAG_STRING && strstr(data, okFlag)) ||
                (okFlagType == RX_FLAG_REGEXP && trx_regexp(data, okFlag) >= 0) ||
                (okFlagType >= RX_FLAG_BINARY && memmem(data, len, okFlag, okFlagType)))
            {
                // 因 OK 标志中止
                return RX_RET_OK;
            }
        }

        if (wlen && failFlag && failFlagType != RX_FLAG_NULL) {
            // 需要考虑 failFlag
            if ((failFlagType == RX_FLAG_STRING && strstr(data, failFlag)) ||
                (failFlagType == RX_FLAG_REGEXP && trx_regexp(data, failFlag) >= 0) ||
                (failFlagType >= RX_FLAG_BINARY && memmem(data, len, failFlag, failFlagType)))
            {
                // 因 Fail 标志中止
                return RX_RET_FAIL;
            }
        }

        if (status == VI_ERROR_TMO) return RX_RET_TIMEOUT;
        return RX_RET_OK;
    }

    return STD_ERR_FAIL;
}

static std_err_t _visatrx_rxline(struct _transceiver* trx, void* buf, size_t size, size_t* len) {
    if(!trx || !trx->hif) return STD_ERR_INVALID_IF_TYPE;
    ViSession dev = (ViSession)trx->hif;
    ViUInt32 wlen = 0;
    ViStatus status = viRead(dev, buf, size, &wlen);
    if (len) *len = wlen;
    return (status < VI_SUCCESS) ? STD_ERR_FAIL : STD_ERR_OK;
}

static std_err_t _visatrx_tx(struct _transceiver* trx, const void* buf, size_t size, size_t* len) {
    if(!trx || !trx->hif) return STD_ERR_INVALID_IF_TYPE;
    ViSession dev = (ViSession)trx->hif;
    ViUInt32 wlen = 0;
    ViStatus status = viWrite(dev, buf, size, &wlen);
    if (len) *len = wlen;
    return (status < VI_SUCCESS) ? STD_ERR_FAIL : STD_ERR_OK;
}

// 打开的所有 VISA 设备数量，当所有设备都被关闭时，关闭共享的 ResourceManager
static int _ndev = 0;
// 共享的 ResourceManager
static ViSession _rm = 0;

static void _visatrx_close(struct _transceiver* trx) {
    if(!trx || !trx->hif) return;
    ViSession dev = (ViSession)trx->hif;
    viClose(dev);
    _ndev--;
    if(_ndev <= 0) viClose(_rm);
}

transceiver_t* visatrx_open(const char* addr) {
    ViSession dev;
    ViStatus status;
    transceiver_t* trx = NULL;

    // 启动VISA
    if (_rm == 0) {
        status = viOpenDefaultRM(&_rm);
        if (status < VI_SUCCESS || !_rm) return NULL;
    }

    // 打开设备通道
    status = viOpen(_rm, addr, VI_NULL, VI_NULL, &dev);
    if (status >= VI_SUCCESS) {
        /* Initialize the timeout attribute to 10 s */
        viSetAttribute(dev, VI_ATTR_TMO_VALUE, 10000);
        /* Set termination character to carriage return (\r=0x0D) */
        viSetAttribute(dev, VI_ATTR_TERMCHAR, 0x0D);
        viSetAttribute(dev, VI_ATTR_TERMCHAR_EN, VI_TRUE);
        /* Don't assert END on the last byte */
        viSetAttribute(dev, VI_ATTR_SEND_END_EN, VI_FALSE);
        // 分配 trx 数据结构
        trx = (transceiver_t*)malloc(sizeof(transceiver_t));
        if (trx) {
            trx->hif = (void*)dev;
            trx->get_mtu = _visatrx_get_mtu;
            trx->clear_rx_buffer = _visatrx_clear_rx_buffer;
            trx->rx = _visatrx_rx;
            trx->rxline = _visatrx_rxline;
            trx->tx = _visatrx_tx;
            trx->close = _visatrx_close;
            _ndev++;
            return trx;
        }
        viClose(dev);
    }

    viClose(_rm);
    return NULL;
}
